%{
#include <iostream>
#include <string>
#include <vector>

  using namespace std;

#include "tipo.h"
#include "tabla.h"
#include "nodo.h"

#define YYERROR_VERBOSE

  extern int yylineno;
  extern int yylex(void);
  extern int yyerror(char*);

%}

%union {
  Nodo* nodo;
  Tipo* tip;
  int ivalor;
  char cvalor;
  string* nom;
  int tipo_valor;
  ListaParametros* listaNodos;
  Entrada* entrada;
}

%type <entrada> nombre_funcion
%type <tip> especificador_de_tipo lista_declarador_inic declarador declaracion_parametro
%type <nodo> lista_de_sentencias sentencia sentencia_compuesta sentencia_de_expresion sentencia_de_seleccion sentencia_de_salto expresion expresion_de_asignacion expresion_de_adicion expresion_de_multiplicacion expresion_unaria expresion_sufija expresion_primaria parte_izquierda
%type <listaNodos> lista_de_expresiones_de_argumentos

%token INT TVOID IF ELSE RETURN INC_OP DEC_OP CHAR
%token <ivalor> CTENT
%token <cvalor> CTCHAR
%token <nom> LITERAL_ALFANUMERICO IDENTIFICADOR

%start unidad_traduccion
%%

/*"unidad_traduccion" es una sucesion de "declaracion_externa" */
unidad_traduccion
	: declaracion_externa				
	| unidad_traduccion declaracion_externa		
	;

/*"declaracion_externa" puede ser o bien la definicion de una funcion ("definicion_funcion"), o una "declaracion"*/
declaracion_externa
	: definicion_de_funcion				
	| declaracion					
	;
/* Declaraciones ---------------------------------------------------------- */

/* Una "declaracion" consta de el especificador de tipo y la lista de declaradores/inicializadores (lista_declarador_inic) */
declaracion
	: especificador_de_tipo lista_declarador_inic';'	
	;

/* Solo vamos a implementar el tipo entero */
especificador_de_tipo
	: TVOID						{$$ = new TipoVoid(); }
	| INT						{$$ = new TipoEntero(); }
	| CHAR						{$$ = new TipoChar(); }
	;

/* Una lista de declaradores es una sucesion de declaradores separada por comas */
lista_declarador_inic
	: declarador ',' {$<tip>$ = $<tip>0} lista_declarador_inic				
	| declarador	
	;

/* Un declarador es un identificador*/
declarador
	: IDENTIFICADOR					{tabla.agregar(*($1), $<tip>0);}
	;


/* Definicion de funcion ------------------------------------*/

/* Definimos una funcion: nombre de funcion mas sentencia compuesta */
definicion_de_funcion
	: nombre_funcion sentencia_compuesta		{prologo(*$1); $2->generar(); epilogo(*$1); tabla.cerrarFuncion(); }
	;

/* Definimos el nombre de funcion como un espcificador de tipo mas un identificador mas 0 o 1 parametros */
nombre_funcion
	: especificador_de_tipo IDENTIFICADOR '(' 	{ tabla.abrirFuncion(*$2, $<tip>1); } declaracion_parametro {tabla.anadeparametro(*$<tip>5,tabla.buscar(*$2));} ')' { $$ = new Entrada(tabla.buscar(*$2)); }
     	| especificador_de_tipo IDENTIFICADOR '(' ')' 	{ tabla.abrirFuncion(*$2, $<tip>1); $$ = new Entrada(tabla.buscar(*$2)); } 
	;

/* Una declaracion de parametro esta compuesta por un especificador de tipo y un identificador, o por VOID */
declaracion_parametro
	: especificador_de_tipo IDENTIFICADOR		{tabla.agregar(*($2), $<tip>1, PARAMETRO); $$=$<tip>1;}
	| TVOID {$$=new TipoVoid;}
	;


/* Definimos la sentencia compuesta (o bloque) como una lista de sentencias, o una lista de declaraciones, o una lista de declaraciones seguida por una lista de sentencias */

sentencia_compuesta
	: '{' lista_de_sentencias '}'						{$$ = $2;}
	| '{' lista_de_declaraciones lista_de_sentencias '}'			{$$ = $3;}
	;

/* Una lista de sentencias es una sucesion de sentencias */
lista_de_sentencias
	: sentencia					{$$ = $1;}
	| lista_de_sentencias sentencia			{$$ = new NodoSecuencia($1,$2);}
	;

/* Una lista de declaraciones es una sucesion de declaraciones */
lista_de_declaraciones
	: declaracion					
	| lista_de_declaraciones declaracion		
	;

/* Una sentencia puede ser compuesta, de expresion, de seleccion o de salto */
sentencia
	: sentencia_compuesta				{$$ = $1;}
	| sentencia_de_expresion			{$$ = $1;}
	| sentencia_de_seleccion			{$$ = $1;}
	| sentencia_de_salto				{$$ = $1;}
	;

/* Como sentencia de seleccion, solo implementamos el if..else */
sentencia_de_seleccion
	: IF '(' expresion ')' sentencia			{$$ = new NodoIf($3, $5);}
	| IF '(' expresion ')' sentencia ELSE sentencia		{$$ = new NodoIfElse($3, $5, $7);}
	;

/* Como sentencia de salto, implementamos return */
sentencia_de_salto
	: RETURN ';'					{$$ = new NodoRetornoVacio();}
	| RETURN expresion ';'				{$$ = new NodoRetorno($2); }
	;

/* Una sentencia de espresion puede estar vacia */
sentencia_de_expresion
	: ';'						{$$ = new NodoVacio();}
	| expresion ';'					{$$ = $1;}
	;

/* Una expresion es una secuencia de al menos una expresion de asignacion */
expresion
	: expresion_de_asignacion			{$$ = $1;}
	| expresion ',' expresion_de_asignacion		{$$ = new NodoSecuencia($1,$3);}
	;

/* Una expresion de asignacion es una expresion unaria, unida el operador de asignacion y otra expresion de asignacion, que puede ser directamente una expresion de adicion  */
expresion_de_asignacion
	: expresion_de_adicion							{$$ = $1;}
	| parte_izquierda operador_de_asignacion expresion_de_asignacion	{$$ = new NodoAsignacion($1,$3);}
	; 

/* El operador de asignacion que utilizamos es el igual (=) */
operador_de_asignacion
	: '='								
	;

/* Una expresion de adicion es una de multplicacion o varias de ellas sumadas o restadas, con asociatividad por la izquierda */
expresion_de_adicion
	: expresion_de_multiplicacion					{$$ = $1;}
	| expresion_de_adicion '+' expresion_de_multiplicacion		{$$ = new NodoSuma($1,$3);}
	| expresion_de_adicion '-' expresion_de_multiplicacion		{$$ = new NodoResta($1,$3);}
	;

/* Una expresion de multiplicacion es una expresion unaria o varias de ellas, multiplicadas, divididas, o halladas su modulo, con asociatividad por la izquierda */
expresion_de_multiplicacion
	: expresion_unaria						{$$ = $1;}
	| expresion_de_multiplicacion '*' expresion_unaria		{$$ = new NodoMult($1,$3);}
	| expresion_de_multiplicacion '/' expresion_unaria		{$$ = new NodoDivision($1,$3);}
	| expresion_de_multiplicacion '%' expresion_unaria		{$$ = new NodoModulo($1,$3);}
	;

/* Una expresion unaria es una expresion sufija, o un operador unario mas una expresion unaria, o una parte izquierda (lvalue) seguida de un operador incremental */
expresion_unaria
	: expresion_sufija						{$$ = $1;}
	| '-' expresion_unaria						{$$ = new NodoMenosUnario($2);}
	| '+' expresion_unaria						{$$ = new NodoMasUnario($2);}
	| INC_OP parte_izquierda					{$$ = new NodoIncPrefijo($2);}
	| DEC_OP parte_izquierda					{$$ = new NodoDecPrefijo($2);}
	;

/* Una expresion sufija es una expresion primaria, una llamada a una funcion, o una parte izquierda (lvalue) seguida de un operador incremental */
expresion_sufija
	: expresion_primaria						{$$ = $1;}
	| IDENTIFICADOR '(' ')'						{$$ = new NodoLlamada($1, new ListaParametros());}
	| IDENTIFICADOR '(' lista_de_expresiones_de_argumentos ')'	{$$ = new NodoLlamada($1, $3);}
	| parte_izquierda INC_OP					{$$ = new NodoIncSufijo($1);}
	| parte_izquierda DEC_OP					{$$ = new NodoDecSufijo($1);}
	;

/* Una lista de expresiones de argumentos es una lista de expresiones de asignacion */
lista_de_expresiones_de_argumentos
	: expresion_de_asignacion						{$$ = new ListaParametros(); $$->push_back($1);}
	| lista_de_expresiones_de_argumentos ',' expresion_de_asignacion	{$$ = $1; $$->push_back($3); }
	;

/* Una expresion primaria es un identificador, una constante (un numero entero, en nuestro caso) , un literal alfanumerico, o una expresion entre parentesis */
expresion_primaria
	: parte_izquierda					{$$ = new NodoLValue($1);}
	| CTENT								{$$ = new NodoConstanteEntera($1);}
	| LITERAL_ALFANUMERICO				{$$ = new NodoConstanteCadena($1);}
	| CTCHAR							{$$ = new NodoConstanteChar($1);}
	| '(' expresion ')'					{$$ = $2;}
	;

/* Una parte izquierda es un identificador */
parte_izquierda
	: IDENTIFICADOR							{$$ = new NodoIdentificadorLVal(tabla.buscar(*$1));}
	; 

%%

int main() 
{
    // Realiza al análisis ascendente, generando a su vez el código de las funciones
    yyparse();
    
    // Genera la sección de datos del programa, llamando a la función generar de la tabla 
    tabla.generar();

    // Genera la seccion .rodata del programa, recorriendo la tabla estática con las constantes
    // de cadenas de caracteres.
    cout << ".section\t.rodata\n";
    for (map<string,int>::iterator i = NodoConstanteCadena::tablaCadenas.begin();
	 i != NodoConstanteCadena::tablaCadenas.end(); i++)
	cout << ".S" << i->second << ":\t.string\t" << i->first << endl;

	
} // fin de main()

int yyerror(char* msg) {
  cerr << "en linea " << yylineno << ": " << msg << endl;
  return 1;
} // fin de yyerror()

